﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CopyDataExtension.cs" company="">
//   
// </copyright>
// <summary>
//   The copy data extension.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Libium.Drawing
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;

    using Libium.Core;

    /// <summary>
    /// The copy data extension.
    /// </summary>
    public static class CopyDataExtension
    {
        #region Public Methods and Operators

        /// <summary>
        /// The get bitmap.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="Bitmap"/>.
        /// </returns>
        /// <exception cref="NotExistsException">
        /// </exception>
        public static Bitmap GetBitmap(this ICopyData data, NotExistsAction action = NotExistsAction.ThrowException)
        {
            var bitmap = data.GetValue<Bitmap>(DataFormats.NetSystemDrawingBitmap, NotExistsAction.ReturnNull)
                         ?? data.GetValue<object>(DataFormats.Bitmap, NotExistsAction.ReturnNull) as Bitmap;

            if (bitmap == null && action == NotExistsAction.ThrowException)
            {
                throw new NotExistsException("Bitmap doesn't exist in the pasted data");
            }

            if (bitmap != null)
            {
                //TODO Temporary hardcode
                var memoryStream = new MemoryStream();
                bitmap.Save(memoryStream, ImageFormat.Png);
                memoryStream.Position = 0;
                bitmap = new Bitmap(memoryStream);
            }

            return bitmap;
        }

        /// <summary>
        /// The get bitmap stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="Stream"/>.
        /// </returns>
        /// <exception cref="NotExistsException">
        /// </exception>
        public static Stream GetBitmapStream(
            this ICopyData data, NotExistsAction action = NotExistsAction.ThrowException)
        {
            var stream = data.GetValue<object>(DataFormats.Bitmap, NotExistsAction.ReturnNull) as Stream;

            if (stream == null && action == NotExistsAction.ThrowException)
            {
                throw new NotExistsException("Bitmap stream doesn't exist in the pasted data");
            }

            return stream;
        }

        /// <summary>
        /// The get dib stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="Stream"/>.
        /// </returns>
        public static Stream GetDibStream(this ICopyData data, NotExistsAction action = NotExistsAction.ThrowException)
        {
            return data.GetValue<MemoryStream>(DataFormats.DeviceIndependentBitmap, action);
        }

        /// <summary>
        /// The get image.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="isValidMimeType">
        /// The is valid mime type.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="IImage"/>.
        /// </returns>
        public static IImage GetImage(
            this ICopyData data, 
            Func<MimeType, bool> isValidMimeType, 
            NotExistsAction action = NotExistsAction.ThrowException)
        {
            var images = GetImages(data, isValidMimeType, action);
            return images == null ? null : images[0];
        }

        /// <summary>
        /// The get image.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="IImage"/>.
        /// </returns>
        public static IImage GetImage(this ICopyData data, NotExistsAction action = NotExistsAction.ThrowException)
        {
            return data.GetImage(IsValidMimeType, action);
        }

        /// <summary>
        /// The get images.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="isValidMimeType">
        /// The is valid mime type.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="IList"/>.
        /// </returns>
        /// <exception cref="NotExistsException">
        /// </exception>
        public static IList<IImage> GetImages(
            this ICopyData data, 
            Func<MimeType, bool> isValidMimeType, 
            NotExistsAction action = NotExistsAction.ThrowException)
        {
            var images = new List<IImage>();
            var bitmap = data.GetBitmap(NotExistsAction.ReturnNull);
            if (bitmap != null)
            {
                images.Add(new NetImage(bitmap));
                return images;
            }

            using (var stream = data.GetBitmapStream(NotExistsAction.ReturnNull))
            {
                if (stream != null)
                {
                    images.Add(new NetImage(stream));
                    return images;
                }
            }

            using (var stream = data.GetDibStream(NotExistsAction.ReturnNull))
            {
                if (stream != null)
                {
                    images.Add(new NetImage(stream, ImageStreamType.DeviceIndependentBitmap));
                    return images;
                }
            }

            var files = data.GetFiles(NotExistsAction.ReturnNull);
            if (files != null)
            {
                foreach (var file in files)
                {
                    if (isValidMimeType(file.GetDescription().MimeType))
                    {
                        images.Add(new NetImage(file));
                    }
                    else
                    {
                        // TODO throw InvalidMimeType exception
                    }
                }

                if (images.Count > 0)
                {
                    return images;
                }
            }

            if (action == NotExistsAction.ThrowException)
            {
                throw new NotExistsException("Image doesn't exist in the pasted data");
            }

            return null;
        }

        /// <summary>
        /// The get images.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// The <see cref="IList"/>.
        /// </returns>
        public static IList<IImage> GetImages(
            this ICopyData data, NotExistsAction action = NotExistsAction.ThrowException)
        {
            return data.GetImages(IsValidMimeType, action);
        }

        /// <summary>
        /// The is bitmap exist.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsBitmapExist(this ICopyData data)
        {
            return data.IsExist(DataFormats.NetSystemDrawingBitmap) || data.IsExist(DataFormats.Bitmap)
                   || data.IsExist(DataFormats.DeviceIndependentBitmap);
        }

        /// <summary>
        /// The is bitmap stream exist.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsBitmapStreamExist(this ICopyData data)
        {
            return data.IsExist(DataFormats.Bitmap);
        }

        /// <summary>
        /// The is dib stream exist.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsDibStreamExist(this ICopyData data)
        {
            return data.IsExist(DataFormats.DeviceIndependentBitmap);
        }

        /// <summary>
        /// The is image exist.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="mimeTypes">
        /// The mime types.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsImageExist(this ICopyData data, IEnumerable<MimeType> mimeTypes = null)
        {
            return IsImagesExist(data, mimeTypes);
        }

        /// <summary>
        /// The is images exist.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="mimeTypes">
        /// The mime types.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsImagesExist(this ICopyData data, IEnumerable<MimeType> mimeTypes = null)
        {
            return data.IsBitmapExist() || data.IsBitmapStreamExist() || data.IsDibStreamExist()
                   || data.IsFileExist(mimeTypes);
        }

        /// <summary>
        /// The set bitmap.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="func">
        /// The func.
        /// </param>
        public static void SetBitmap(this ICopyData data, Func<Bitmap> func)
        {
            data.SetValue(DataFormats.NetSystemDrawingBitmap, func);
        }

        /// <summary>
        /// The set bitmap stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="func">
        /// The func.
        /// </param>
        public static void SetBitmapStream(this ICopyData data, Func<Stream> func)
        {
            data.SetValue(DataFormats.Bitmap, func);
        }

        /// <summary>
        /// The set bitmap stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="stream">
        /// The stream.
        /// </param>
        public static void SetBitmapStream(this ICopyData data, Stream stream)
        {
            SetBitmapStream(data, () => new MemoryStream(stream.CopyToArray()));
        }

        /// <summary>
        /// The set bitmap stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="filePath">
        /// The file path.
        /// </param>
        public static void SetBitmapStream(this ICopyData data, string filePath)
        {
            SetBitmapStream(data, () => new MemoryStream(File.OpenRead(filePath).CopyToArray()));
        }

        /// <summary>
        /// The set dib stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="func">
        /// The func.
        /// </param>
        public static void SetDibStream(this ICopyData data, Func<Stream> func)
        {
            data.SetValue(DataFormats.DeviceIndependentBitmap, func);
        }

        /// <summary>
        /// The set dib stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="stream">
        /// The stream.
        /// </param>
        public static void SetDibStream(this ICopyData data, Stream stream)
        {
            SetDibStream(data, () => stream);
        }

        /// <summary>
        /// The set dib stream.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="filePath">
        /// The file path.
        /// </param>
        public static void SetDibStream(this ICopyData data, string filePath)
        {
            SetDibStream(data, File.OpenRead(filePath));
        }

        /// <summary>
        /// The set image.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="func">
        /// The func.
        /// </param>
        public static void SetImage(this ICopyData data, Func<IImage> func)
        {
            data.SetBitmapStream(() => func().GetStream());

            // TODO Add Dib stream
            // data.SetBitmapStream(image.GetDibReadStream());
            data.SetFile(() => func().GetFile());
        }

        /// <summary>
        /// The set image.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="image">
        /// The image.
        /// </param>
        public static void SetImage(this ICopyData data, IImage image)
        {
            var s = image.GetStream();
            SetImage(data, () => image);
        }

        #endregion

        #region Methods

        /// <summary>
        /// The is valid mime type.
        /// </summary>
        /// <param name="mimeType">
        /// The mime type.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private static bool IsValidMimeType(MimeType mimeType)
        {
            return mimeType.ContentType == MimeTypes.Bmp.ContentType
                   || mimeType.ContentType == MimeTypes.Emf.ContentType
                   || mimeType.ContentType == MimeTypes.Exif.ContentType
                   || mimeType.ContentType == MimeTypes.Gif.ContentType
                   || mimeType.ContentType == MimeTypes.Icon.ContentType
                   || mimeType.ContentType == MimeTypes.Jpg.ContentType
                   || mimeType.ContentType == MimeTypes.Png.ContentType
						 || mimeType.ContentType == MimeTypes.Tiff.ContentType;
        }

        #endregion
    }
}